<template>
  <div class="container">
    <h1 class="title">🍜 智能识别美食图片</h1>

    <!-- 上传区域 -->
    <div class="upload-section">
      <input 
        type="file" 
        @change="handleImageUpload" 
        accept="image/*"
        class="upload-input"
        ref="fileInput"
      >
      <div 
        class="upload-area"
        @dragover.prevent
        @drop="handleDrop"
        @click="handleAreaClick"
        :class="{ 'has-image': previewImage }"
      >
        <div v-if="previewImage" class="image-preview">
          <img :src="previewImage" class="preview-image">
          <div class="hover-mask">
            <span class="reupload-text">点击更换图片</span>
          </div>
        </div>
        
        <div v-else class="upload-prompts">
          <svg class="upload-icon" viewBox="0 0 24 24">
            <path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
          </svg>
          <p class="upload-tip">点击或拖放图片上传</p>
          <p class="upload-subtip">支持 JPG/PNG 格式</p>
        </div>
      </div>
    </div>

    <!-- 加载状态 -->
    <div v-if="isLoading" class="loading">
      <div class="spinner"></div>
      <p class="loading-text">AI正在分析中...</p>
    </div>

    <!-- 识别结果 -->
    <div v-if="result && !isLoading" class="result-flow">
      <div class="header-section">
        <h2 class="food-name">{{ result.name }}</h2>
        <div class="rating-box">
          <div class="score-text">{{ result.score }}/10</div>
          <div class="star-rating">
            <div class="star-background">★★★★★</div>
            <div 
              class="star-overlay" 
              :style="{ width: `${(result.score / 10) * 100}%` }"
            >★★★★★</div>
          </div>
        </div>
      </div>

      <div class="info-grid">
        <div class="info-item">
          <span class="label">🔥 热量</span>
          <span class="value">{{ result.calories }} kcal</span>
        </div>
        <div class="info-item">
          <span class="label">⏱ 准备时间</span>
          <span class="value">{{ result.prepTime }}</span>
        </div>
        <div class="info-item">
          <span class="label">🌍 地域偏爱</span>
          <span class="value">{{ result.region }}</span>
        </div>
      </div>

      <div class="info-block">
        <h3><span class="icon">👩🍳</span> 制作步骤</h3>
        <ol class="step-list">
          <li v-for="(step, index) in result.cookingMethod" :key="index">
            <span class="step-number">{{ index + 1 }}</span>
            {{ step }}
          </li>
        </ol>
      </div>

      <div class="info-block">
        <h3><span class="icon">🥗</span> 原料分析</h3>
        <div class="ingredient-analysis">
          <div 
            v-for="(group, title) in result.ingredients" 
            :key="title"
            class="ingredient-group"
          >
            <h4>{{ title }}</h4>
            <div class="ingredient-tags">
              <span 
                v-for="(item, idx) in group" 
                :key="idx"
                class="tag"
              >{{ item }}</span>
            </div>
          </div>
        </div>
      </div>

      <div class="info-block">
        <h3><span class="icon">📜</span> 美食历史</h3>
        <p class="history">{{ result.history }}</p>
      </div>
    </div>

    <div v-if="error" class="error">
      ❌ {{ errorMessage }}
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue';

const previewImage = ref(null);
const isLoading = ref(false);
const result = ref(null);
const error = ref(false);
const errorMessage = ref('');
const fileInput = ref(null);

// 处理文件上传
const handleImageUpload = async (e) => {
  const file = e.target.files[0];
  if (!file) return;

  try {
    isLoading.value = true;
    error.value = false;
    
    // 读取文件为Base64
    const base64Data = await readFileAsBase64(file);
    
    // 创建预览图
    previewImage.value = base64Data;

    // 获取识别结果
    result.value = await recognizeFoodAPI(base64Data);
  } catch (err) {
    error.value = true;
    errorMessage.value = err.message || '识别失败，请重试';
  } finally {
    isLoading.value = false;
  }
};

// 文件转Base64工具函数
const readFileAsBase64 = (file) => {
  return new Promise((resolve, reject) => {
    // 添加文件类型验证（先于读取操作）
    const allowedTypes = ['image/jpeg', 'image/png', 'image/jpg'];
    if (!allowedTypes.includes(file.type.toLowerCase())) {
      reject(new Error('仅支持 JPG/PNG 格式图片'));
      return;
    }

    const reader = new FileReader();
    
    reader.onload = () => {
      // 在此处获取结果并验证
      const base64Data = reader.result;
      
      // 验证DataURL格式
      if (!/^data:image\/(jpeg|png|jpg);base64,/.test(base64Data)) {
        reject(new Error('无效的图片格式'));
        return;
      }
      
      resolve(base64Data);
    };
    
    reader.onerror = error => reject(error);
    reader.readAsDataURL(file);
  });
};

// 调用识别API
const recognizeFoodAPI = async (base64Data) => {
  try {
    // 保留完整的DataURL（包含data:image/...前缀）
    const fullBase64 = base64Data;

    // 验证DataURL格式
    const base64Regex = /^data:image\/(png|jpeg|jpg);base64,/i;
    if (!base64Regex.test(fullBase64)) {
      throw new Error('无效的图片格式，仅支持PNG/JPEG/JPG');
    }

    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), 30000);

    // 修改请求体结构
    const response = await fetch('http://localhost:3000/api/glm4v/analyze', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        imageBase64: fullBase64 // 字段名改为imageBase64
      }),
      signal: controller.signal
    });

    clearTimeout(timeoutId);

    // 增强HTTP错误处理
    if (!response.ok) {
      const errorData = await response.json().catch(() => ({}));
      throw new Error(errorData.error || `HTTP错误 ${response.status}`);
    }

    // 严格验证响应格式
    const contentType = response.headers.get('content-type') || '';
    if (!contentType.includes('application/json')) {
      throw new Error('服务器返回了非JSON响应');
    }

    const responseData = await response.json();
    
    // 处理业务逻辑错误
    if (!responseData.success) {
      throw new Error(responseData.error || '识别服务异常');
    }

    return responseData.data;

  } catch (err) {
    console.error('完整请求日志:', {
      error: err.message,
      request: {
        endpoint: '/api/glm4v/analyze',
        payloadType: 'imageBase64'
      }
    });
    throw new Error(`请求失败: ${err.message}`);
  }
}

// 点击上传区域
const handleAreaClick = () => {
  if (!previewImage.value) {
    fileInput.value.click();
  }
};

// 处理拖放
const handleDrop = (e) => {
  e.preventDefault();
  const file = e.dataTransfer.files[0];
  if (file) handleImageUpload({ target: { files: [file] } });
};



</script>

  
<style lang="less" scoped>
@primary-color: #FF6B6B;
@secondary-color: #FFB88C;
@background-light: #f8f9fa;
@text-dark: #333;
@shadow-color: rgba(0, 0, 0, 0.1);

// 混合宏
.mixin-transition(@property: all, @duration: 0.3s, @easing: cubic-bezier(0.4, 0, 0.2, 1)) {
  transition: @property @duration @easing;
}

.mixin-box-shadow(@x: 0, @y: 2px, @blur: 8px, @color: @shadow-color) {
  box-shadow: @x @y @blur @color;
}

// 容器样式
.container {
  max-width: 800px;
  margin: 2rem auto;
  padding: 2rem;
  background: @background-light;
  min-height: 100vh;
  font-family: 'Segoe UI', system-ui, sans-serif;

  .title {
    font-size: 2.8rem;
    color: @primary-color;
    text-align: center;
    margin: 2rem 0;
    text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);
  }
}

// 上传区域
.upload-section {
  margin: 3rem 0;

  .upload-input {
    display: none;
  }

  .upload-area {
    position: relative;
    min-height: 320px;
    border: 2px dashed @secondary-color;
    border-radius: 16px;
    .mixin-transition();
    background: white;
    .mixin-box-shadow();
    cursor: pointer;

    &:hover {
      border-color: @primary-color;
      transform: translateY(-2px);
      box-shadow: 0 4px 12px rgba(255, 107, 107, 0.2);
    }

    &.has-image {
      border-style: solid;
      border-color: @primary-color;
    }

    .hover-mask {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      background: rgba(0,0,0,0.4);
      display: none;
      align-items: center;
      justify-content: center;
      color: white;
      backdrop-filter: blur(2px);
    }

    &:hover .hover-mask {
      display: flex;
    }
  }

  .image-preview {
    height: 100%;
    border-radius: 14px;
    overflow: hidden;

    .preview-image {
      width: 100%;
      height: 100%;
      object-fit: cover;
      max-height: 500px;
    }
  }

  .upload-prompts {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    text-align: center;

    .upload-icon {
      width: 64px;
      height: 64px;
      margin-bottom: 1.5rem;
      fill: @primary-color;
    }

    .upload-tip {
      font-size: 1.2rem;
      color: #666;
      margin-bottom: 0.5rem;
      font-weight: 500;
    }

    .upload-subtip {
      color: #999;
      font-size: 0.9rem;
    }
  }
}

// 加载状态
.loading {
  text-align: center;
  padding: 3rem;
  background: white;
  border-radius: 16px;
  .mixin-box-shadow();
  margin: 2rem 0;

  .spinner {
    width: 3rem;
    height: 3rem;
    border: 4px solid #f3f3f3;
    border-top: 4px solid @primary-color;
    border-radius: 50%;
    animation: spin 1s linear infinite;
    margin: 0 auto 1rem;
  }

  .loading-text {
    color: #666;
    font-size: 1.1rem;
  }
}

// 识别结果
.result-flow {
  background: white;
  border-radius: 16px;
  .mixin-box-shadow(0, 4px, 6px, rgba(0, 0, 0, 0.05));
  padding: 2.5rem;
  margin: 2rem 0;

  .header-section {
    text-align: center;
    margin-bottom: 2.5rem;

    .food-name {
      font-size: 2.2rem;
      color: @text-dark;
      margin-bottom: 1.2rem;
      position: relative;
      display: inline-block;
      padding-bottom: 0.5rem;

      &::after {
        content: '';
        position: absolute;
        bottom: 0;
        left: 50%;
        transform: translateX(-50%);
        width: 60px;
        height: 3px;
        background: @primary-color;
      }
    }
  }
}

// 评分模块
.rating-box {
  display: inline-flex;
  align-items: center;
  gap: 1.2rem;
  background: linear-gradient(135deg, #FFF5F5, #FFE8E8);
  padding: 1rem 2rem;
  border-radius: 24px;

  .score-text {
    font-size: 1.6rem;
    font-weight: 700;
    color: @primary-color;
  }

  .star-rating {
    position: relative;
    font-size: 1.6rem;

    .star-background {
      color: #e0e0e0;
    }

    .star-overlay {
      position: absolute;
      top: 0;
      left: 0;
      color: #FFD700;
      overflow: hidden;
      white-space: nowrap;
    }
  }
}

// 信息网格
.info-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 1.5rem;
  margin: 2.5rem 0;

  .info-item {
    background: white;
    padding: 1.5rem;
    border-radius: 12px;
    text-align: center;
    .mixin-box-shadow(0, 2px, 4px, rgba(0, 0, 0, 0.08));
    .mixin-transition(transform, 0.2s, ease);

    &:hover {
      transform: translateY(-3px);
    }

    .label {
      color: @primary-color;
      font-size: 1rem;
      font-weight: 600;
      display: block;
      margin-bottom: 0.8rem;
    }

    .value {
      color: #444;
      font-size: 1.2rem;
      font-weight: 500;
    }
  }
}

// 信息区块
.info-block {
  background: white;
  border-radius: 12px;
  padding: 2rem;
  margin: 2rem 0;
  .mixin-box-shadow(0, 3px, 6px, rgba(0, 0, 0, 0.06));
  .mixin-transition(box-shadow, 0.3s, ease);

  &:hover {
    box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
  }

  h3 {
    color: #2c3e50;
    font-size: 1.4rem;
    margin-bottom: 1.5rem;
    padding-bottom: 0.8rem;
    border-bottom: 2px solid @secondary-color;
    display: flex;
    align-items: center;
    gap: 0.8rem;
  }

  .step-list {
    padding-left: 1rem;

    li {
      background: @background-light;
      padding: 1.2rem 1.8rem;
      border-radius: 8px;
      margin: 1.2rem 0;
      position: relative;
      .mixin-box-shadow(0, 1px, 3px, rgba(0, 0, 0, 0.05));
      line-height: 1.6;

      .step-number {
        position: absolute;
        left: -1.2rem;
        top: 50%;
        transform: translateY(-50%);
        background: @primary-color;
        color: white;
        width: 28px;
        height: 28px;
        border-radius: 50%;
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 0.9rem;
        font-weight: 600;
      }
    }
  }
}

// 原料分析
.ingredient-analysis {
  display: flex;
  flex-direction: column;
  gap: 1.5rem;

  .ingredient-group h4 {
    color: #666;
    margin-bottom: 0.8rem;
    font-size: 1.1rem;
  }

  .ingredient-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 0.8rem;

    .tag {
      background: linear-gradient(145deg, #FFE8E8, #FFD4D4);
      color: @primary-color;
      padding: 0.4rem 1rem;
      border-radius: 20px;
      font-size: 0.95rem;
      font-weight: 500;
      border: 1px solid rgba(255, 107, 107, 0.15);
      .mixin-transition(all, 0.2s, ease);

      &:hover {
        transform: scale(1.05);
        .mixin-box-shadow(0, 2px, 4px, rgba(255, 107, 107, 0.2));
      }
    }
  }
}

// 历史介绍
.history {
  background: @background-light;
  padding: 1.5rem;
  border-radius: 8px;
  border-left: 4px solid @primary-color;
  line-height: 1.8;
  color: #555;
}

// 错误提示
.error {
  color: #ff4444;
  background: #ffeeee;
  padding: 1.2rem;
  border-radius: 8px;
  text-align: center;
  margin: 2rem 0;
  .mixin-box-shadow(0, 2px, 4px, rgba(255, 68, 68, 0.1));
}

// 动画
@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}
  </style>